#include "opplib.h"

/********************************************************
  opp_list
 ********************************************************/

opp_list *opp_list_new()
{
  opp_list *temp = NULL;
  temp = (opp_list *) malloc(sizeof(opp_list));
  if (temp != NULL) {
    temp->data = NULL;
    temp->prev = NULL;
    temp->next = NULL;
  }
  return temp;
}

uint8_t opp_list_delete(opp_list * list)
{
  opp_list *temp = NULL;
  opp_list *tprev = NULL;
  opp_list *tnext = NULL;
  if (list == NULL)
    return 1;
  temp = list;
  tnext = list->prev;
  while (temp != NULL) {
    tprev = temp;
    temp = temp->next;
    free(tprev->data);
    free(tprev);
  }
  temp = tnext;
  while (temp != NULL) {
    tnext = temp;
    temp = temp->prev;
    free(tprev->data);
    free(tprev);
  }
  return 0;
}

uint8_t opp_list_delete_list_only(opp_list * list)
{
  opp_list *temp = NULL;
  opp_list *tprev = NULL;
  opp_list *tnext = NULL;
  if (list == NULL)
    return 1;
  temp = list;
  tnext = list->prev;
  while (temp != NULL) {
    tprev = temp;
    temp = temp->next;
    free(tprev);
  }
  temp = tnext;
  while (temp != NULL) {
    tnext = temp;
    temp = temp->prev;
    free(tnext);
  }
  return 0;
}

opp_list *opp_list_first(opp_list * list)
{
  opp_list *temp = list;
  if (temp == NULL)
    return NULL;
  while (temp->prev != NULL) {
    temp = temp->prev;
  }
  return temp;
}

opp_list *opp_list_last(opp_list * list)
{
  opp_list *temp = list;
  if (temp == NULL)
    return NULL;
  while (temp->next != NULL) {
    temp = temp->next;
  }
  return temp;
}

void *opp_list_get(opp_list * list, int32_t i)
{
  opp_list *temp = list;
  int32_t c = 0;
  if (list == NULL)
    return NULL;
  if (list->prev != NULL)
    temp = opp_list_first(list);
  for (; c < i && temp != NULL; c++)
    temp = temp->next;
  if (temp == NULL || c != i)
    return (void *)"ERROR";
  return temp->data;
}

uint8_t opp_list_set(opp_list * list, int32_t i, void *data)
{
  opp_list *temp = list;
  int32_t c = 0;
  if (list == NULL)
    return 1;
  if (list->prev != NULL)
    temp = opp_list_first(list);
  for (; c < i && temp != NULL; c++)
    temp = temp->next;
  if (temp == NULL || c != i)
    return 2;
  free(temp->data);
  temp->data = data;
  return 0;
}

opp_list *opp_list_append(opp_list * list, void *data)
{
  opp_list *temp = list;
  if (list == NULL) {
    list = (opp_list *) malloc(sizeof(opp_list));
    if (list == NULL)
      return NULL;
    list->data = data;
    list->next = NULL;
    list->prev = NULL;
  } else {
    if (temp->next != NULL)
      temp = opp_list_last(list);
    temp->next = opp_list_new();
    temp->next->data = data;
    temp->next->prev = temp;
  }
  return list;
}

opp_list *opp_list_prepend(opp_list * list, void *data)
{
  if (list == NULL) {
    list = (opp_list *) malloc(sizeof(opp_list));
    if (list == NULL)
      return NULL;
    list->data = data;
    list->next = NULL;
    list->prev = NULL;
  } else {
    if (list->prev != NULL)
      list = opp_list_first(list);
    list->prev = opp_list_new();
    list->prev->data = data;
    list->prev->next = list;
    list = list->prev;
  }
  return list;
}

opp_list *opp_list_remove(opp_list * list, int32_t i)
{
  opp_list *temp = list;
  opp_list *ret = list;
  int32_t c = 0;
  if (list == NULL)
    return NULL;
  if (list->prev != NULL)
    temp = opp_list_first(list);
  for (; c < i && temp != NULL; c++) {
    temp = temp->next;
  }
  if (temp == NULL || c != i)
    return NULL;
  if (temp->prev != NULL)
    temp->prev->next = temp->next;
  temp->next->prev = temp->prev;
  if (temp == list)
    ret = temp->next;
  free(temp->data);
  free(temp);
  return ret;
}

void opp_list_cut(opp_list * list)
{
  opp_list *temp = NULL;
  opp_list *next = NULL;
  opp_list *prev = NULL;
  temp = list;
  prev = list->prev;
  while (temp != NULL) {
    next = temp->next;
    free(temp->data);
    free(temp);
    temp = next;
  }
  prev->next = NULL;
}

void opp_list_cut_list_only(opp_list * list)
{
  opp_list *temp = NULL;
  opp_list *next = NULL;
  opp_list *prev = NULL;
  temp = list;
  prev = list->prev;
  while (temp != NULL) {
    next = temp->next;
    free(temp);
    temp = next;
  }
  prev->next = NULL;
}

uint32_t opp_list_length(opp_list * list)
{
  opp_list *temp = list;
  uint32_t c = 0;
  if (list == NULL)
    return 0;
  if (list->prev != NULL)
    temp = opp_list_first(list);
  while (temp != NULL) {
    c++;
    temp = temp->next;
  }
  return c;
}

/********************************************************
  opp_node
 ********************************************************/

opp_node *opp_node_new()
{
  opp_node *temp = NULL;
  temp = (opp_node *) malloc(sizeof(opp_node));
  if (temp != NULL) {
    temp->data = NULL;
    temp->son = NULL;
    temp->dad = NULL;
    temp->pbro = NULL;
    temp->bro = NULL;
  }
  return temp;
}

uint8_t opp_node_delete(opp_node * node)
{
  opp_node *root = NULL;
  if (node == NULL)
    return 1;
  root = opp_node_root(node);
  if (root->son != NULL)
    opp_node_remove(root->son);
  free(root->data);
  free(root);
  return 0;
}

uint8_t opp_node_delete_tree_only(opp_node * node)
{
  opp_node *root = NULL;
  if (node == NULL)
    return 1;
  root = opp_node_root(node);
  if (root->son != NULL)
    opp_node_remove(root->son);
  free(root);
  return 0;
}

uint8_t opp_node_remove(opp_node * node)
{
  if (node == NULL)
    return 1;
  if (node->son != NULL)
    opp_node_remove(node->son);
  if (node->bro != NULL)
    opp_node_remove(node->bro);
  free(node->data);
  free(node);
  return 0;
}

opp_node *opp_node_root(opp_node * node)
{
  opp_node *temp = NULL;
  if (node == NULL)
    return NULL;
  temp = node;
  while (temp->dad != NULL)
    temp = temp->dad;
  return temp;
}

uint8_t opp_node_set(opp_node * node, void *data)
{
  if (node == NULL)
    return 1;
  free(node->data);
  node->data = data;
  return 0;
}

opp_node *opp_node_add_son(opp_node * node, void *son)
{
  opp_node *temp = NULL;
  if (node == NULL)
    return NULL;
  if (node->son == NULL) {
    temp = opp_node_new();
    if (temp == NULL)
      return NULL;
    node->son = temp;
    temp->data = son;
    temp->dad = node;
  } else {
    return opp_node_add_bro(node->son, son);
  }
  return temp;
}

opp_node *opp_node_link_son(opp_node * node, opp_node * son)
{
  if (node == NULL)
    return NULL;
  if (node->son == NULL) {
    node->son = son;
    son->dad = node;
    son->bro = NULL;
    son->pbro = NULL;
  } else {
    return opp_node_link_bro(node->son, son);
  }
  return son;
}

opp_node *opp_node_add_bro(opp_node * node, void *bro)
{
  opp_node *temp = NULL;
  opp_node *new_node = NULL;
  if (node == NULL)
    return NULL;
  temp = node;
  while (temp->bro != NULL)
    temp = temp->bro;
  new_node = opp_node_new();
  if (new_node == NULL)
    return NULL;
  temp->bro = new_node;
  new_node->pbro = temp;
  new_node->data = bro;
  new_node->dad = temp->dad;
  return new_node;
}

opp_node *opp_node_link_bro(opp_node * node, opp_node * bro)
{
  opp_node *temp = NULL;
  if (node == NULL)
    return NULL;
  temp = node;
  while (temp->bro != NULL)
    temp = temp->bro;
  temp->bro = bro;
  bro->pbro = temp;
  bro->dad = temp->dad;
  bro->bro = NULL;
  return bro;
}
